import asyncio
import os
import shutil
import aiofiles

BUFFER_SIZE_KIB = 4096


async def igrone(*args, **kwargs):
    pass


def get_size(size_bytes):  # 文件大小加单位
    if (size_bytes > 1024*1024*1024*1024):
        return f"{size_bytes/(1024*1024*1024*1024):.2f}TiB"
    elif (size_bytes > 1024*1024*1024):
        return f"{size_bytes/(1024*1024*1024):.2f}GiB"
    elif (size_bytes > 1024*1024):
        return f"{size_bytes/(1024*1024):.2f}MiB"
    elif (size_bytes > 1024):
        return f"{size_bytes/(1024):.2f}KiB"
    else:
        return f"{size_bytes}B"


async def move_function(move_file, write_log, write_progress, exit_process, wait_button):

    size_cnt = 0  # 操作文件总大小
    file_lists = []  # 文件操作列表
    # (0,src,dst,size) 复制文件
    # (1,dir) 创建文件夹
    # (2,src,dst) 复制软链接文件
    copied_size = 0  # 操作过的大小
    write_progress_func = igrone
    dist_dir = ""  # 目标
    last_choose = 0  # 用于全部覆盖选择
    error_mode = 0  # 是否出错

    async def copy_single_file(src: str, dst: str, size: int):
        nonlocal copied_size, size_cnt, error_mode

        now_copied_size = 0
        try:
            async with aiofiles.open(src, 'rb') as fsrc:
                try:
                    async with aiofiles.open(dst, 'wb') as fdst:
                        while True:
                            # 读buffer
                            buf = await fsrc.read(BUFFER_SIZE_KIB*1024)
                            if not buf:
                                break
                            await fdst.write(buf)  # 写文件
                            now_copied_size += len(buf)
                            progress = ((copied_size+now_copied_size)
                                        * 100 // size_cnt)  # 计算进度
                            await (write_progress_func(progress))
                except PermissionError:
                    await write_log("权限不足："+dst+"\n")
                    error_mode = 1
                except Exception as e:
                    await write_log("其它错误["+repr(e)+"]："+dst+"\n")
                    error_mode = 1
        except PermissionError:
            await write_log("权限不足："+src+"\n")
            error_mode = 1
        except Exception as e:
            await write_log("其它错误["+repr(e)+"]："+src+"\n")
            error_mode = 1
        copied_size += size

    async def mkdir(path):
        nonlocal error_mode
        now_error_str = ""  # 传递报错信息

        def mkdir_func(path):  # 新建文件夹同步函数
            nonlocal now_error_str
            try:  # 异常处理
                os.makedirs(path, exist_ok=True)
            except PermissionError:
                now_error_str = "权限不足："+path
            except Exception as e:
                now_error_str = "其它错误["+repr(e)+"]："+path
        await (asyncio.to_thread(mkdir_func, path))
        if (now_error_str != ""):  # 异常处理
            await write_log(now_error_str+"\n")
            error_mode = 1

    async def copy_link(src: str, dst: str):  # 软链接处理
        nonlocal error_mode
        now_error_str = ""

        def copy_link_func(src, dst):
            nonlocal now_error_str
            try:
                link_target = os.readlink(src)
                os.symlink(link_target, dst)
            except PermissionError:
                now_error_str = "权限不足："+dst
            except Exception as e:
                now_error_str = "其它错误["+repr(e)+"]："+dst
        await (asyncio.to_thread(copy_link_func, src, dst))
        if (now_error_str != ""):
            await write_log(now_error_str+"\n")
            error_mode = 1

    async def search_file(origin_path, dist, now_path=""):  # 递归搜索文件夹
        nonlocal size_cnt, file_lists, last_choose
        if (origin_path == "/"):  # 特殊处理根目录
            origin_path = ""
        if (dist == "/"):
            dist = ""

        file_lists.append((1, dist+now_path))  # 新建文件夹
        for file in await (asyncio.to_thread(lambda: os.listdir(origin_path+now_path))):
            file_path = origin_path + now_path + os.sep + file
            if (dist + now_path+os.sep +
                    file == file_path):
                await write_log("尝试覆盖自身: "+dist + now_path+os.sep +
                                file+"\n")
                continue
            if (await (asyncio.to_thread(lambda: os.path.exists(dist + now_path+os.sep+file)))):
                await write_log("文件已存在: "+dist + now_path+os.sep +
                                file+"\n")
                if (last_choose == 1):
                    continue
                elif (last_choose == 2):
                    pass
                else:
                    # 操作按钮
                    choose = await wait_button(["跳过", "覆盖", "全部跳过", "全部覆盖"])
                    if (choose == 0):
                        continue
                    elif (choose == 1):
                        pass
                    elif (choose == 2):
                        last_choose = 1
                        continue
                    elif (choose == 3):
                        last_choose = 2
            if await (asyncio.to_thread(lambda: os.path.isfile(file_path))):
                file_lists.append((0, file_path, dist + now_path+os.sep +
                                   file, os.path.getsize(file_path)))
                # 计算大小
                size_cnt += await (asyncio.to_thread(lambda: os.path.getsize(file_path)))
            elif await (asyncio.to_thread(lambda: os.path.islink(file_path))):  # 软链接
                file_lists.append((2, file_path, dist + now_path+os.sep +
                                   file, os.path.getsize(file_path)))
            else:
                await (search_file(
                    origin_path, dist, now_path=now_path+os.sep+file))

    async def do_copy(move_file, write_log, write_progress, exit_process, wait_button):
        async def copy_callback():  # 进行复制
            nonlocal size_cnt, write_progress_func, file_lists, dist_dir, last_choose
            write_progress_func = write_progress
            dist_dir = move_file.dist
            if (not await (asyncio.to_thread(lambda: os.path.exists(move_file.dist)))):  # 检测路径
                await write_log("路径不存在: "+move_file.dist+"\n")
                await write_progress(100)
                await exit_process(True)
                return
            if (len(move_file.origin) == 1):  # 单个文件，自动重命名
                origin = move_file.origin[0]
                if (move_file.dist == "/"):  # 根目录处理
                    move_file.dist = ""
                dist = move_file.dist+os.sep + \
                    os.path.basename(move_file.origin[0])
                if (not await (asyncio.to_thread(lambda: os.path.exists(move_file.origin[0])))):
                    await write_log("文件不存在: "+move_file.origin[0]+"\n")
                    await write_progress(100)
                    await exit_process(True)
                    return
                if (await (asyncio.to_thread(lambda: os.path.exists(move_file.dist+os.sep+os.path.basename(move_file.origin[0]))))):

                    def rename_copied_file(file_abspath, add_str="-copy"):  # 自动重命名
                        file_paths = os.path.dirname(file_abspath)
                        if (file_paths == "/"):
                            file_paths = ""
                        file_fullname = os.path.basename(file_abspath)
                        file_path, file_ext = os.path.splitext(file_fullname)
                        return file_paths+os.sep+file_path+add_str+file_ext
                    new_dist = rename_copied_file(dist)
                    rename_file_id = 1
                    while (await (asyncio.to_thread(lambda: os.path.exists(new_dist)))):  # 序号增加
                        new_dist = rename_copied_file(
                            dist, "-copy"+str(rename_file_id))
                        rename_file_id += 1
                    await write_log("文件已存在: "+dist+"，重命名到 "+new_dist+"\n")
                    dist = new_dist
                if (await (asyncio.to_thread(lambda: os.path.isfile(origin)))):
                    file_lists.append(
                        (0, origin, dist, await (asyncio.to_thread(lambda: os.path.getsize(origin)))))
                    size_cnt = await (asyncio.to_thread(lambda: os.path.getsize(origin)))
                elif (os.path.islink(origin)):  # 软链接处理
                    file_lists.append(
                        (2, origin, dist))
                else:
                    await write_log("搜索文件中..."+"\n")
                    await search_file(origin, dist)
            else:
                await write_log("搜索文件中..."+"\n")
                for i in move_file.origin:
                    origin = i
                    if (move_file.dist == "/"):
                        move_file.dist = ""
                    dist = (move_file.dist+os.sep +
                            os.path.basename(i)).rstrip("/")
                    if (not (await asyncio.to_thread(lambda: os.path.exists(origin)))):
                        await write_log("文件不存在: "+origin+"\n")
                        continue
                    if (await asyncio.to_thread(lambda: os.path.isdir(origin))):
                        if (origin == dist):
                            await write_log("尝试覆盖自身: "+dist+"\n")
                            continue
                        await search_file(i, dist)
                    else:
                        if (origin == dist):
                            await write_log("尝试覆盖自身: "+dist+"\n")
                            continue
                        if (await asyncio.to_thread(lambda: os.path.exists(dist))):
                            await write_log("文件已存在: "+dist+"\n")
                            if (last_choose == 1):
                                continue
                            elif (last_choose == 2):
                                pass
                            else:
                                choose = await wait_button(["跳过", "覆盖", "全部跳过", "全部覆盖"])
                                if (choose == 0):
                                    continue
                                elif (choose == 1):
                                    pass
                                elif (choose == 2):
                                    last_choose = 1
                                    continue
                                elif (choose == 3):
                                    last_choose = 2
                        file_lists.append(
                            (0, origin, dist, await (asyncio.to_thread(lambda: os.path.getsize(origin)))))
                        size_cnt += os.path.getsize(origin)

            await write_log("总计 "+get_size(size_cnt)+"\n")  # 打印日志
            for i in file_lists:  # 执行任务
                if (i[0] == 0):
                    await write_log(i[1]+"\n")
                    await copy_single_file(i[1], i[2], i[3])
                elif (i[0] == 1):
                    await mkdir(i[1])
                else:
                    await copy_link(i[1], i[2])
                await asyncio.sleep(0.1)

        await copy_callback()
    if (move_file.types == "move"):  # 移动处理
        # 是否同盘
        if (await asyncio.to_thread(lambda: os.stat(move_file.origin[0]).st_dev) == await asyncio.to_thread(lambda: os.stat(move_file.dist).st_dev)):
            j = -1
            for i in move_file.origin:  # 快速移动
                j += 1
                await write_progress(j*100 // len(move_file.origin))
                origin = i
                if (move_file.dist == "/"):
                    move_file.dist = ""
                dist = move_file.dist+os.sep + \
                    os.path.basename(i)
                if (not (await asyncio.to_thread(lambda: os.path.exists(origin)))):
                    await write_log("文件不存在: "+origin+"\n")
                    continue
                if (await asyncio.to_thread(lambda: os.path.isdir(origin))):
                    if (origin == dist):
                        await write_log("尝试覆盖自身: "+dist+"\n")
                        continue
                    await search_file(i, dist)
                else:
                    if (origin == dist):
                        await write_log("尝试覆盖自身: "+dist+"\n")
                        continue
                    if (await asyncio.to_thread(lambda: os.path.exists(dist))):
                        await write_log("文件已存在: "+dist+"\n")
                        if (last_choose == 1):
                            continue
                        elif (last_choose == 2):
                            pass
                        else:
                            choose = await wait_button(["跳过", "覆盖", "全部跳过", "全部覆盖"])
                            if (choose == 0):
                                continue
                            elif (choose == 1):
                                pass
                            elif (choose == 2):
                                last_choose = 1
                                continue
                            elif (choose == 3):
                                last_choose = 2

                        now_error_str = ""

                        def rmtree(dist):
                            nonlocal now_error_str
                            try:
                                if (os.path.isdir(dist)):
                                    shutil.rmtree(dist)
                                else:
                                    os.remove(dist)
                            except PermissionError:
                                now_error_str = "权限不足："+dist
                            except Exception as e:
                                now_error_str = "其它错误["+repr(e)+"]："+dist
                        await asyncio.to_thread(lambda: rmtree(dist))
                        if (now_error_str != ""):
                            await write_log(now_error_str+"\n")
                await write_log(origin+"\n")
                now_error_str = ""

                def f_move(dist):
                    nonlocal now_error_str
                    try:
                        shutil.move(origin, dist)
                    except PermissionError:
                        now_error_str = "权限不足："+dist
                    except Exception as e:
                        now_error_str = "其它错误["+repr(e)+"]："+dist
                await asyncio.to_thread(lambda: f_move(dist))
                if (now_error_str != ""):
                    await write_log(now_error_str+"\n")
        else:  # 先复制后删除
            await do_copy(move_file, write_log, write_progress, exit_process, wait_button)
            for i in move_file.origin:
                now_error_str = ""

                def rmtree(dist):  # 删除目录
                    nonlocal now_error_str
                    try:
                        if (os.path.isdir(dist)):
                            shutil.rmtree(dist)
                        else:
                            os.remove(dist)
                    except PermissionError:
                        now_error_str = "权限不足："+dist
                    except Exception as e:
                        now_error_str = "其它错误["+repr(e)+"]："+dist
                await asyncio.to_thread(lambda: rmtree(i))
                if (now_error_str != ""):
                    await write_log(now_error_str+"\n")
    else:
        await do_copy(move_file, write_log, write_progress, exit_process, wait_button)

    await write_progress(100)
    if (error_mode == 1):
        await exit_process(True)
    else:
        await exit_process(False)
